package com.XSQS.storage.index;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.XSQS.storage.helper.FileName;
import com.XSQS.storage.manager.FileManager;
import com.XSQS.util.Utility;

public class InvertedReader {
	private Logger logger=Utility.getLogger(InvertedReader.class);
	
	private static InvertedReader instance;
	
	private FileManager fm;
	
	private RandomAccessFile entriesFile;
	private RandomAccessFile wordItemsFile;
	@SuppressWarnings("unused")
	private RandomAccessFile metaWordItemsFile;

	private InvertedReader() {
		fm=FileManager.getInstance();
	}

	public synchronized static InvertedReader getInstance() {
		if (instance == null) {
			instance = new InvertedReader();
		}
		return instance;
	}
	
	public void open(){
		entriesFile=fm.openFile(FileName.getIndexEntriesFile());
		wordItemsFile=fm.openFile(FileName.getWordItemsFile());
		metaWordItemsFile=fm.openFile(FileName.getMetaWordItemsFile());
	}
	
	/**
	 * Read only one IndexEntry which is specified.
	 * @param index the index of the specified IndexEntry that should be found 
	 * @return the specified IndexEntry
	 */
	public IndexEntry readIndexEntry(int index){
		
		if(index==InvertedUtil.NULL){
			return null;
		}
		byte[] entryByte = new byte[IndexEntry.LENGTH];

		IndexEntry entry = new IndexEntry();

		try {
			entriesFile.seek(InvertedUtil.getEntriesFilePointer(index));
			entriesFile.readFully(entryByte);
		} catch (IOException e) {
			logger.error(e.getMessage());
		}
		entry.build(entryByte);
		
		return entry;
	}
	
	/**
	 * Read IndexEntry list which are pointed by a same key word, starting from the specified one
	 * @param index the start index of the IndexEntry list
	 * @return the specified IndexEntry list
	 */
	public IndexEntry[] readIndexEntries(int index){
		int count = 0,next = 0;
		List<IndexEntry> list = new ArrayList<IndexEntry>();
		IndexEntry entry = readIndexEntry(index);
		if(entry==null){
			return null;
		}
		do{
			list.add(entry);
			next = entry.getNext();
			entry = readIndexEntry(next);
			count++;
		}while (next!=InvertedUtil.NULL);
		
		IndexEntry[] entries= new IndexEntry[count];
		
		for(int i= 0; i<count;i++){
			entries[i] = (IndexEntry) list.get(i);
		}
		
		return entries;
	}
	
	/**
	 * Read all IndexEntries in the IndexEntries.index file
	 * @return
	 */
	public IndexEntry[] readAllIndexEntries(){
		
		int count = getNumOfIndexEntries();
		
		IndexEntry[] entries= new IndexEntry[count];
		
		byte[] entryByte = new byte[IndexEntry.LENGTH];

		IndexEntry entry = new IndexEntry();
		for(int index=0; index<count;index++){
			try {
				entriesFile.seek(InvertedUtil.getEntriesFilePointer(index));
				entriesFile.readFully(entryByte);
			} catch (IOException e) {
				logger.error(e.getMessage());
			}
			entry.build(entryByte);
			entries[index] = entry;
		}
		return entries;
	}
	
	protected int getNumOfIndexEntries(){
		long length = 0;
		try {
			length = entriesFile.length();
		} catch (IOException e) {
			logger.error(e.getMessage());
			return -1;
		}
		if( (length-InvertedUtil.ENTRIES_FILE_HEAD_LEN)%IndexEntry.LENGTH!=0){
			logger.error("The IndexEntries file contains unexpected data!");
			return -1;
		}
		int count = (int) (length-InvertedUtil.ENTRIES_FILE_HEAD_LEN)/IndexEntry.LENGTH;
		return count;
	}
	
	/**
	 * Read only one InvertedWordItem which is specified.
	 * @param index the index of the specified InvertedWordItem 
	 * @return the specified InvertedWordItem
	 */
	public InvertedWordItem readInvertedWordItem(int index){
		return null;
	}
	
	/**
	 * Read all InvertedWordItems in the WordItems.index file
	 * @return
	 */
	public InvertedWordItem[] readAllInvertedWordItem(){
		int count = 0;
		List<InvertedWordItem> list = new ArrayList<InvertedWordItem>();
		
		try {
			wordItemsFile.seek(0);
			InvertedWordItem wordItem = readAInvertedWordItem();
			while(wordItem!=null){
				list.add(wordItem);
				count++;
				wordItem =readAInvertedWordItem();
			}
		} catch (IOException e) {
			logger.error(e.getMessage());
		}
		InvertedWordItem[] wordItems= new InvertedWordItem[count];
		
		for(int i= 0; i<count;i++){
			wordItems[i] = (InvertedWordItem) list.get(i);
		}
		return wordItems;
	}
	
	
	private InvertedWordItem readAInvertedWordItem() throws IOException{
		int keyLength=wordItemsFile.read();
		if(keyLength==-1){
			return null;
		}
		if(keyLength<0||keyLength>256){
			IOException e=new IOException("Read bad data when reading a InvertedWordItem");
			throw e;
		}
		byte [] buf=new byte[keyLength+InvertedWordItem.ENTRY_POINTER_LENGTH*2];
		wordItemsFile.read(buf);
		return new InvertedWordItem(buf);
	}
	
	public void close(){
		fm.closeFile(FileName.getIndexEntriesFile());
		fm.closeFile(FileName.getWordItemsFile());
		fm.closeFile(FileName.getMetaWordItemsFile());
	}
	
}
